package com.cxyjy.travelguide.controller;

import com.alibaba.dashscope.app.Application;
import com.alibaba.dashscope.app.ApplicationParam;
import com.alibaba.dashscope.app.ApplicationResult;
import com.alibaba.dashscope.app.ApplicationUsage;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.reactivex.Flowable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

// AI问答，流式调用方法
@RestController
@Slf4j // 自动生成 log 变量
@RequestMapping("/v1")
public class AliyunAiController {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Value("${dashscope.chat-api-key}")
    private String apiKey;

    @Value("${dashscope.chat-app-id}")
    private String appId;

    @PostMapping(value ="/aliyun-ai-chat",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<ProcessedResponse>> handleStreamRequest(@RequestBody Map<String, Object> request) {

        String text= (String) request.get("text");
        if (ObjectUtils.isEmpty(text)) {
            return Flux.just(
                    ServerSentEvent.<ProcessedResponse>builder()
                            .event("303error")
                            .data(ProcessedResponse.error("调用失败，text的值必选且不能为空不能为空！"))
                            .build()
            );
        }

        //  构造历史消息汇总
        Map <String,String> newmessage=new HashMap<>();
        Object messages=request.get("message");
        List<Map<String,String>> message=new ArrayList<>();
        List<Message> originmessages = new ArrayList<>();
        try {
            // 安全转换：将未知类型的 rawMessages 转换为 List<Message>
            if (messages != null) {
                message = objectMapper.convertValue(
                        messages,
                        new TypeReference<List<Map<String, String>>>() {
                        }
                );
                originmessages= objectMapper.convertValue(
                        messages,
                        new TypeReference<List<Message>>() {
                        }
                );
            }
        } catch (IllegalArgumentException e) {
            return Flux.just(
                    ServerSentEvent.<ProcessedResponse>builder()
                            .event("304error")
                            .data(ProcessedResponse.error("调用失败，可能错误原因：传入message字段格式不正确"))
                            .build()
            );
        }
        if (originmessages.isEmpty())
        {
            originmessages = new ArrayList<>();
            originmessages.add(Message.builder().role("system").content("你是一位为用户解决行程规划难题的助手，会积极使用知识库中的相关知识，为用户合理安排出行计划，会考虑到景点开放时间、相应城市对应时间的交通状况等因素，周到地为用户提供回复。不超过1500字").build());
            message=new ArrayList<>();
            newmessage.put("role", "system");
            newmessage.put("content", "你是一位为用户解决行程规划难题的助手，会积极使用知识库中的相关知识，为用户合理安排出行计划，会考虑到景点开放时间、相应城市对应时间的交通状况等因素，周到地为用户提供回复。不超过1500字");
            message.add(newmessage);
        }
        originmessages.add(Message.builder().role("user").content(text).build());
        newmessage=new HashMap<>();
        newmessage.put("role", "user");
        newmessage.put("content", text);
        message.add(newmessage);

        //配置大模型参数

        Application application = new Application();
        ApplicationParam param = ApplicationParam.builder()
                .appId(appId) // 替换为实际的应用ID
                .apiKey(apiKey)
                .prompt(text)
                .hasThoughts(true)
                .incrementalOutput(true)
                .messages(originmessages)
                .build();

        //准备流式调用
        StringBuilder answerBuilder = new StringBuilder();
        AtomicReference<String> requestId = new AtomicReference<>();
        AtomicInteger inputTokens = new AtomicInteger();
        AtomicInteger outputTokens = new AtomicInteger();
        List<Map<String, String>> finalMessage = message;
        try {
            //原始流式调用
            Flowable<ApplicationResult> rawStream = application.streamCall(param);
            return Flux.from(rawStream)
                    .doOnNext(result -> {
                        //对每个流式返回结果进行处理，汇总回答，统计消耗tokens
                        String contentChunk = result.getOutput().getText();
                        answerBuilder.append(contentChunk);
                        ApplicationUsage.ModelUsage model = result.getUsage().getModels().get(0);
                        if (requestId.get() == null) {
                            requestId.set(result.getRequestId());
                            inputTokens.set(ObjectUtils.isEmpty(model.getInputTokens()) ? 0 : model.getInputTokens());

                        }
                        outputTokens.set(ObjectUtils.isEmpty(model.getOutputTokens()) ? 0 : model.getOutputTokens());
                    })
                    .map(this::wrapSseEvent)    //处理单个流事件
                    .concatWith( // 在流结束后追加最终汇总事件
                            Mono.defer(() -> {
                                // 构造最终 answer
                                String finalAnswer = answerBuilder.toString();
                                // 添加到 messages 列表
                                Map<String, String> assistantMsg = new HashMap<>();
                                assistantMsg.put("role", "assistant");
                                assistantMsg.put("content", finalAnswer);
                                finalMessage.add(assistantMsg);
                                // 构造最终 SSE 事件（包含元数据和完整 messages）
                                return Mono.just(buildFinalSseEvent(
                                        requestId.get(),
                                        inputTokens.get(),
                                        outputTokens.get(),
                                        finalMessage,
                                        finalAnswer
                                ));
                            })
                    )
                    .doOnError(ex -> {
                        log.error("Stream processing failed", ex);
                    })
                    .onErrorResume(this::handleError);

        }catch (ApiException  e) {
            System.err.println("流式调用出错");
            return Flux.just(
                    ServerSentEvent.<ProcessedResponse>builder()
                            .event("302error")
                            .data(ProcessedResponse.error("可能错误原因：应用API、apikey错误"))
                            .build()
            );
        }
        catch (NoApiKeyException e) {
            System.err.println("流式调用出错");
            return Flux.just(
                    ServerSentEvent.<ProcessedResponse>builder()
                            .event("302error")
                            .data(ProcessedResponse.error("可能错误原因：没有ApiKey"))
                            .build()
            );
        } catch (InputRequiredException e) {
            System.err.println("流式调用出错");
            return Flux.just(
                    ServerSentEvent.<ProcessedResponse>builder()
                            .event("302error")
                            .data(ProcessedResponse.error("可能错误原因：缺少必要输入"))
                            .build()
            );
        }
    }
    //最终结果处理方法
    private ServerSentEvent<ProcessedResponse> buildFinalSseEvent(
            String requestId,
            int inputTokens,
            int outputTokens,
            List<Map<String, String>> messages,
            String finalAnswer
    ) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("requestID", requestId);
        payload.put("text", finalAnswer);
        payload.put("inputTokens", inputTokens);
        payload.put("outputTokens", outputTokens);
        payload.put("messages", messages);
        return ServerSentEvent.<ProcessedResponse>builder()
                .event("complete") // 自定义事件类型标识完成
                .data(processResult(payload,finalAnswer))
                .build();
    }
    //最终返回SSE结果项构造
    private ProcessedResponse processResult(Map<String, Object> payload,String finalAnswer) {
        return ProcessedResponse.builder()
//                .content(finalAnswer)
//                .timestamp(Instant.now())
                .metadata(payload)
                .build();
    }

    // 封装为 SSE 事件
    private ServerSentEvent<ProcessedResponse> wrapSseEvent(ApplicationResult result) {
        return ServerSentEvent.<ProcessedResponse>builder()
                .event("data")
                .data(processResultItem(result))
                .build();
    }

    // 处理单个结果项
    private ProcessedResponse processResultItem(ApplicationResult result) {
        ApplicationUsage.ModelUsage model = result.getUsage().getModels().get(0);
        Map<String, Object> data = new HashMap<>();
//        metadata.put("session_id",result.getOutput().getSessionId());
        data.put("inputTokens", model.getInputTokens());
        data.put("outputTokens", model.getOutputTokens());
        return ProcessedResponse.builder()
                .content(result.getOutput().getText())
//                .timestamp(Instant.now())
                .metadata(data)
                .build();
    }

    // 错误处理
    private Flux<ServerSentEvent<ProcessedResponse>> handleError(Throwable ex) {
        return Flux.just(
                ServerSentEvent.<ProcessedResponse>builder()
                        .event("300error")
                        .data(ProcessedResponse.error("阿里接口错误，返回信息："+ex.getMessage()+'\n'+"请检查输入！"))
                        .build()
        );
    }
}
